#ifndef SPW_PAIR_IN_H_
#define SPW_PAIR_IN_H_

#include <type_traits>
#include <utility/spw_move_in.h>

namespace spwstd{
namespace spwuti{
// pair 储存任意结构类型的结构体
template<typename T1, typename T2>
struct Pair{
   using FirstType = T1;
   using SecondType = T2;

   FirstType first_;
   SecondType second_;

   // 构造函数
   constexpr Pair(): first_{}, second_{} {}
   constexpr Pair(const T1& a, const T2& b): first_{a}, second_{b} {}

   // 复制构造
   constexpr Pair(const Pair&) = default;

   // 移动构造
   constexpr Pair(Pair&&) = default;

    // 复制构造
    // U1和U2可以隐式转换成T1和T2
    // p.first_ 可以转换为first_，使用{}初始化所以不支持窄化转化，如long->int
    template<typename U1, typename U2, 
                typename= typename std::enable_if<
                    std::__and_<
                        std::is_convertible<const U1&, T1>, 
                        std::is_convertible<const U2&, T2>
                    >::value
                >::type
            >
    constexpr Pair(const Pair<U1,U2>& p): first_{p.first_}, second_{p.second_} {}

    // 支持通用引用, [避免用通用引用重载]
    template<typename U1,
        typename = typename std::enable_if<
            std::is_convertible<U1,T1>::value
        >::type >
    constexpr Pair(U1&& x, const T2& y)
        : first_{spwuti::forward<U1>(x)}, second_{y} {}

    template<typename U2,
        typename = typename std::enable_if<
            std::is_convertible<U2,T2>::value
        >::type >
    constexpr Pair(const T1& x, U2&& y)
        : first_{x}, second_{spwuti::forward<U2>(y)} {}

    template<typename U1, typename U2,
        typename = typename std::enable_if<
            std::__and_<
                std::is_convertible<U1,T1>,
                std::is_convertible<U2,T2>
            >::value
        >::type >
    constexpr Pair(U1&& x, U2&& y)
        : first_{spwuti::forward<U1>(x)}, second_{spwuti::forward<U2>(y)} {}

    template<typename U1, typename U2,
        typename = typename std::enable_if<
            std::__and_<
                std::is_convertible<U1,T1>,
                std::is_convertible<U2,T2>
            >::value
        >::type >
    constexpr Pair(Pair<U1,U2>&& p)
        : first_{spwuti::forward<U1>(p.first_)}, second_{spwuti::forward<U2>(p.second_)} {}

    // 复制运算符
    Pair& operator=(const Pair& p) {
        first_ = p.first_;
        second_ = p.second_;
        return *this;
    }

    // 移动，不抛出异常
    Pair& operator=(Pair&& p) noexcept {
        // 右值，如果p.first_的=支撑移动,则移动
        first_ = spwuti::forward<FirstType>(p.first_);
        second_ = spwuti::forward<SecondType>(p.second_);
        return *this;
    }

    template<typename U1, typename U2>
    Pair& operator=(const Pair<U1, U2>& p) {
        first_ = p.first_;
        second_ = p.second_;
        return *this;
    }

    template<typename U1, typename U2>
    Pair& operator=(const Pair<U1, U2>&& p) {
        first_ = spwuti::forward<FirstType>(p.first_);
        second_ = spwuti::forward<SecondType>(p.second_);
        return *this;
    }

    void Swap(Pair& p) noexcept {
        spwuti::swap(first_, p.first_);
        spwuti::swap(second_, p.second_);
    }
};

// 逻辑运算符
// 类型必须完全相同。没有提供可转换类型比较的模板

template<typename T1, typename T2>
inline constexpr bool
operator==(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return x.first_ == y.first_ && x.second_ == y.second_;
}

template<typename T1, typename T2>
inline constexpr bool
operator!=(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return x.first_ != y.first_ || x.second_ != y.second_;
}

template<typename T1, typename T2>
inline constexpr bool
operator<(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return x.first_ < y.first_ ||(x.first_ == y.first_&& x.second_ < y.second_);
}

template<typename T1, typename T2>
inline constexpr bool
operator>(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return x.first_ > y.first_ ||(x.first_ == y.first_&& x.second_ > y.second_);
}
template<typename T1, typename T2>
inline constexpr bool
operator<=(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return !(x > y);
}

template<typename T1, typename T2>
inline constexpr bool
operator>=(const Pair<T1, T2>& x, const Pair<T1, T2>& y)
{
    return !(x < y);
}

template<typename T1, typename T2>
inline void
swap(Pair<T1, T2>& x, Pair<T1, T2>& y) noexcept {
    x.Swap(y);
}

// make pair
// 不希望按引用传递
// 值传递
template<typename T1, typename T2>
constexpr Pair<T1, T2> makePair(T1 x, T2 y)
{
    return Pair<T1, T2>(x,y);
}
} // namespace spwuti
} // namespace spwstd
#endif // SPW_PAIR_IN_H_
